//=======================================================================//
//= Include files.													    =//
//=======================================================================//
#include "WorkThread.h"
#include "Encoder.h"
#include "Rasterizer.h"
#include "Settings.h"

//=======================================================================//
//= Data type define.                                                   =//
//=======================================================================//
DEFINE_LOCAL_EVENT_TYPE(wxEVT_WORK_START);
DEFINE_LOCAL_EVENT_TYPE(wxEVT_WORK_END);
DEFINE_LOCAL_EVENT_TYPE(wxEVT_TEXT_CONV);
DEFINE_LOCAL_EVENT_TYPE(wxEVT_IMAGE_CONV);
wxIMPLEMENT_DYNAMIC_CLASS(WorkStartEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(WorkEndEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(TextConvEvent, wxEvent);
wxIMPLEMENT_DYNAMIC_CLASS(ImageConvEvent, wxEvent);
//=======================================================================//
//= Function define.										            =//
//=======================================================================//
WorkStartEvent::WorkStartEvent(int iID)
: wxEvent(iID, wxEVT_WORK_START)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_iTotal = 0;
}

WorkStartEvent::WorkStartEvent(const WorkStartEvent& clsSource)
: wxEvent(clsSource)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_iTotal = clsSource.GetTotal();
}

WorkEndEvent::WorkEndEvent(int iID)
: wxEvent(iID, wxEVT_WORK_END)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Do nothing. */
}

WorkEndEvent::WorkEndEvent(const WorkStartEvent& clsSource)
: wxEvent(clsSource)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Do nothing. */
}

TextConvEvent::TextConvEvent(int iID)
: wxEvent(iID, wxEVT_TEXT_CONV)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_iTotal = 0;
	m_iProcessed = 0;
}

TextConvEvent::TextConvEvent(const TextConvEvent& clsSource)
: wxEvent(clsSource)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_iTotal = clsSource.GetTotal();
	m_iProcessed = clsSource.GetProcessed();
	m_clsMonoCharData = clsSource.GetData();
}

ImageConvEvent::ImageConvEvent(int iID)
: wxEvent(iID, wxEVT_IMAGE_CONV)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Do nothing. */
}

ImageConvEvent::ImageConvEvent(const ImageConvEvent& clsSource)
: wxEvent(clsSource)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_clsMonoImageData = clsSource.GetData();
}

WorkThreadMgr::WorkThreadMgr(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_pclsEvtHandler = nullptr;
	m_pclsThread = nullptr;
	m_iID = wxID_ANY;
}

WorkThreadMgr::~WorkThreadMgr(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	StopThread();
}

WorkThreadMgr& WorkThreadMgr::_getInstance(void)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	static WorkThreadMgr s_clsGlobalInsatnce;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	return s_clsGlobalInsatnce;
}

WorkThreadMgr& WorkThreadMgr::Instance(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	return _getInstance();
}

WorkThreadMgr& WorkThreadMgr::Instance(int iID)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	return _getInstance().SetID(iID);
}

bool WorkThreadMgr::StartThread(wxEvtHandler* pclsEvtHandle, WorkThreadBase* pclsThread)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	bool                bReturn = true;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Check thread status. */
	m_clsCriticalSection.Enter();
	if((nullptr != m_pclsThread) && (true == m_pclsThread->IsRunning()))
	{
		/* Thread is running. */
		bReturn = false;
	}
	m_clsCriticalSection.Leave();

	if(bReturn)
	{
		m_pclsEvtHandler = pclsEvtHandle;
		m_pclsThread = pclsThread;
		if(nullptr != m_pclsThread)
		{
			m_pclsThread->SetOwner(*this);
			bReturn = (wxTHREAD_NO_ERROR == m_pclsThread->Run());
		}
		else
		{
			bReturn = false;
		}
	}

	return bReturn;
}

bool WorkThreadMgr::StopThread(void)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	bool                bReturn;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Release thread. */
	{
		wxCriticalSectionLocker clsLock(m_clsCriticalSection);
		if(nullptr != m_pclsThread)
		{
			bReturn = (wxTHREAD_NO_ERROR == m_pclsThread->Delete());
		}
		else
		{
			bReturn = false;
		}
	}
	/* Waiting for thread stop. */
	while(/* true == */bReturn)
	{
		wxCriticalSectionLocker clsLock(m_clsCriticalSection);
		if(nullptr == m_pclsThread)
		{
			break;
		}
		wxThread::This()->Sleep(1);
	}

	return bReturn;
}

void WorkThreadMgr::OnThreadStop(void)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	WorkEndEvent			clsEvent(GetID());

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Post stop event. */
	PostEvent(clsEvent);
	/* Cleanup thread pointer. */
	wxCriticalSectionLocker clsLock(m_clsCriticalSection);
	m_pclsThread = nullptr;
}

void WorkThreadMgr::PostEvent(const wxEvent& clsEvent)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(nullptr != m_pclsEvtHandler)
	{
		wxPostEvent(m_pclsEvtHandler, clsEvent);
	}
}

bool WorkThreadMgr::IsRunning(void)
{
    /*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	bool                    bReturn;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Cleanup thread pointer. */
	wxCriticalSectionLocker clsLock(m_clsCriticalSection);
	/* Judge threa dis running. */
	if(m_pclsThread)
    {
        bReturn = m_pclsThread->IsRunning();
    }
    else
    {
        bReturn = false;
    }

    return bReturn;
}

WorkThreadBase::WorkThreadBase(void)
: wxThread(wxTHREAD_DETACHED)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_pclsOwner = nullptr;
}

WorkThreadBase::~WorkThreadBase(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(nullptr != m_pclsOwner)
	{
		m_pclsOwner->OnThreadStop();
	}
}

void WorkThreadBase::PostEvent(wxEvent& clsEvent)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(nullptr != m_pclsOwner)
	{
		clsEvent.SetId(m_pclsOwner->GetID());
		m_pclsOwner->PostEvent(clsEvent);
	}
}

void WorkThreadBase::SetOwner(WorkThreadMgr& clsOwner)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_pclsOwner = &clsOwner;
}

TextWorkThread::TextWorkThread(const wxString& strText)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_strTextResource = strText;
}

TextWorkThread::~TextWorkThread(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Do nothing. */
}

wxThread::ExitCode TextWorkThread::Entry(void)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxThread::ExitCode		ulExitCode = (wxThread::ExitCode)0;
	bool					bExit = false;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(0 == m_strTextResource.Length())
	{
		/* No text waiting for process. */
		ulExitCode = (wxThread::ExitCode)1;
	}
	else
	{
		/* Visible character existed. */
		wxUniChar			wcCharCode;
		Encoder				clsEncoder;
		CharacterRasterizer	clsConvertData;
		size_t				sDataIndex = 0; /* Only converted character. */
		wxString::iterator  clsIter;

		/* Update encoder settings. */
		clsEncoder.SetConfig(GlobalEncorderSettings());
		/* Update paint font. */
		clsConvertData.SetFont(GlobalPaintFont());
		/* Remove control character & invisible character. */
		clsIter = m_strTextResource.begin();
		while(clsIter != m_strTextResource.end())
		{
			if(wxIscntrl(*clsIter))
			{
				clsIter = m_strTextResource.erase(clsIter);
			}
			else
			{
				clsIter++;
			}
		}
		/* Post work thread start event. */
		OnThreadStart(m_strTextResource.Length());
		/* Initialize iterator. */
		clsIter = m_strTextResource.begin();
		/* Start thread works. */
		while((false == wxThread::TestDestroy()) && (false == bExit))
		{
			if(clsIter != m_strTextResource.end())
			{
				wcCharCode = *clsIter;
				/* Ignore control characters. */
				if(false == wxIscntrl(wcCharCode))
				{
					RasterizedMonoChar clsMonoCharData(sDataIndex, wcCharCode);
					TextConvEvent clsEvent;
					clsEvent.SetTotal(m_strTextResource.Length());

					/* Initialize/Clear rasterizer. */
					clsConvertData.Clear();
					/* Paint the character and rasterize to bitmap. */
					clsConvertData.Convert(wcCharCode);
					/* Convert monochrome bitmap to font data  */
					clsEncoder.Encode(clsConvertData.AsBitmap().ConvertToImage(), clsMonoCharData);
					/* Post converted data. */
					//m_pclsHandler->AddPendingEvent(clsMonoCharData);
					sDataIndex++; /* Only increase with visible character. */
					clsEvent.SetProcessed(sDataIndex);
					clsEvent.SetData(clsMonoCharData);
					WorkThreadBase::PostEvent(clsEvent);
				}
				clsIter++;
			}
			else
			{
				/* End of string, exit process. */
				bExit = true;
				break;
			}
			wxThread::Sleep(1);
		}
	}
	return ulExitCode;
}

void TextWorkThread::OnThreadStart(int iTotal)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	WorkStartEvent			clsEvent(WorkThreadBase::Owner()->GetID());

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Set total value. */
	clsEvent.SetTotal(iTotal);
	/* Post stop event. */
	PostEvent(clsEvent);
}

TextWorkThread* TextWorkThread::New(const wxString& strText)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	return new TextWorkThread(strText);
}

ImageWorkThread::ImageWorkThread(const wxImage& clsImage)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_clsImageResource = clsImage;
}

ImageWorkThread::~ImageWorkThread(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Do nothing. */
}

wxThread::ExitCode ImageWorkThread::Entry(void)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxThread::ExitCode		ulExitCode = (wxThread::ExitCode)0;
	Encoder					clsEncoder;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Update encoder settings. */
	clsEncoder.SetConfig(GlobalEncorderSettings());
	/* Post work thread start event. */
	OnThreadStart(1);
	/* Start thread works. */
	if(false == wxThread::TestDestroy())
	{
		RasterizedMonoImage	clsImageData;
		ImageConvEvent		clsEvent;


		/* Convert image to pixelated data. */
		clsEncoder.Encode(m_clsImageResource, clsImageData);
		/* Post converted data. */
		clsEvent.SetData(clsImageData);
		WorkThreadBase::PostEvent(clsEvent);
	}
	return ulExitCode;
}

void ImageWorkThread::OnThreadStart(int iTotal)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	WorkStartEvent			clsEvent(WorkThreadBase::Owner()->GetID());

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	/* Set total value. */
	clsEvent.SetTotal(iTotal);
	/* Post stop event. */
	PostEvent(clsEvent);
}

ImageWorkThread* ImageWorkThread::New(const wxImage& clsImage)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	return new ImageWorkThread(clsImage);
}
